package org.example;

import redis.clients.jedis.params.SetParams;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.*;

/**
 * 带降级功能的缓存系统
 * 集成命令行交互界面
 *
 * @author fxab
 * @date 2025/04/06
 */
public class FallbackCache {
    private final RedisClient client;
    private final Map<String, String> localCache = new ConcurrentHashMap<>();
    private final Map<String, RedisLock> activeLocks = new ConcurrentHashMap<>();

    /**
     * redis是否可用
     */
    private volatile boolean redisAvailable = true;
    /**
     * 是否运行
     */
    private volatile boolean running = true;

    /**
     * 带本地缓存的交互界面
     *
     * @param config 配置
     */
    public FallbackCache(ConnectionConfig config) {
        this.client = new RedisClient(config);
        startHealthCheckThread();
        startCommandLoopThread();
    }

    /**
     * 启动健康检查线程
     */
    private void startHealthCheckThread() {
        new Thread(() -> {
            while (running) {
                try {
                    boolean previous = redisAvailable;
                    redisAvailable = client.isAvailable();

                    // 状态变化通知
                    if (previous && !redisAvailable) {
                        System.out.println("\n[警告] Redis不可用，启用本地缓存");
                    } else if (!previous && redisAvailable) {
                        System.out.println("\n[通知] Redis已恢复，切换回Redis");
                    }
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        }, "Health-Checker").start();
    }


    /**
     * 启动命令行交互线程
     */
    private void startCommandLoopThread() {
        new Thread(() -> {
            Scanner scanner = new Scanner(System.in);
            printWelcome();

            while (running) {
                try {
                    System.out.print("> ");
                    String input = scanner.nextLine().trim();
                    if (input.isEmpty()) continue;

                    String[] parts = input.split("\\s+");
                    String command = parts[0].toUpperCase();

                    switch (command) {
                        case "SET": handleSet(parts); break;
                        case "GET": handleGet(parts); break;
                        case "MGET": handleMGet(parts); break;
                        case "DEL": handleDel(parts); break;
                        case "LOCK":
                        case "ALOCK":
                            handleLock(parts); break;
                        case "UNLOCK": handleUnlock(parts); break;
                        case "EXIT": shutdown(); break;
                        case "HELP": printHelp(); break;
                        default:
                            System.out.println("未知命令，输入 HELP 查看帮助");
                    }
                } catch (Exception e) {
                    System.out.println("错误: " + e.getMessage());
                }
            }
            scanner.close();
        }, "Command-Processor").start();
    }

    //------------------------ 命令处理方法 ------------------------//

    /**
     * set指令处理
     *
     * @param parts 零件
     */
    private void handleSet(String[] parts) {
        if (parts.length < 3) {
            throw new IllegalArgumentException("语法: SET key value [NX] [EX seconds]");
        }

        SetParams params = SetParams.setParams();
        for (int i = 3; i < parts.length; i++) {
            String param = parts[i].toUpperCase();
            switch (param) {
                case "NX":
                    params.nx();
                    break;
                case "EX":
                    if (i + 1 >= parts.length) {
                        throw new IllegalArgumentException("EX参数需要指定秒数");
                    }
                    params.ex(Long.parseLong(parts[++i]));
                    break;
                default:
                    throw new IllegalArgumentException("不支持的参数: " + param);
            }
        }

        String result = set(parts[1], parts[2], params);
        System.out.println(result);
    }

    /**
     * get指令处理
     *
     * @param parts 零件
     */
    private void handleGet(String[] parts) {
        if (parts.length != 2) {
            throw new IllegalArgumentException("语法: GET key");
        }
        String value = get(parts[1]);
        System.out.println(value != null ? "\"" + value + "\"" : "(nil)");
    }

    /**
     * MGet指令处理
     *
     * @param parts 零件
     */
    private void handleMGet(String[] parts) {
        if (parts.length < 2) {
            throw new IllegalArgumentException("语法: MGET key1 [key2...]");
        }
        List<String> values = mget(Arrays.copyOfRange(parts, 1, parts.length));
        for (int i = 0; i < values.size(); i++) {
            System.out.printf("%d) %s\n", i + 1,
                    values.get(i) != null ? "\"" + values.get(i) + "\"" : "(nil)");
        }
    }

    /**
     * del指令处理
     *
     * @param parts 零件
     */
    private void handleDel(String[] parts) {
        if (parts.length < 2) {
            throw new IllegalArgumentException("语法: DEL key1 [key2...]");
        }
        Long count = del(Arrays.copyOfRange(parts, 1, parts.length));
        System.out.println("(integer) " + count);
    }

    /**
     * 锁的统一入口
     *
     * @param parts 零件
     */ //第一步
    private void handleLock(String[] parts) {
        if (parts.length != 3) {
            throw new IllegalArgumentException("语法: LOCK/ALOCK key ttl_seconds");
        }
        lock(parts[0], parts[1], parts[2]);
    }





    //------------------------ 核心处理方法 ------------------------//

    /**
     * set方法核心处理方式
     *
     * @param key    钥匙
     * @param value  值
     * @param params params
     * @return {@link String }
     */
    public String set(String key, String value, SetParams params) {
        try {
            if (redisAvailable) {
                return client.set(key, value, params);
            }
            // 降级到本地缓存
            boolean isNx = params != null && hasNxParam(params);
            if (isNx) {
                if (!localCache.containsKey(key)) {
                    localCache.put(key, value);
                    return "OK";
                }
                return null;
            }
            localCache.put(key, value);
            return "OK";
        } catch (RedisClient.RedisException e) {
            redisAvailable = false;
            return handleFallbackSet(key, value, params);
        }
    }

    /**
     * 反射检查NX参数
     *
     * @param params params
     * @return boolean
     */
    private boolean hasNxParam(SetParams params) {
        try {
            // Jedis 4.x 参数存储结构
            Field paramsField = params.getClass().getSuperclass().getDeclaredField("params");
            paramsField.setAccessible(true);
            Map<String, String> paramMap = (Map<String, String>) paramsField.get(params);
            return paramMap.containsKey("nx");
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 本地缓存(set处理过程中redis报错)
     *
     * @param key    钥匙
     * @param value  值
     * @param params params
     * @return {@link String }
     */
    private String handleFallbackSet(String key, String value, SetParams params) {
        boolean isNx = params != null && hasNxParam(params);
        if (isNx) {
            return localCache.putIfAbsent(key, value) == null ? "OK" : null;
        }
        localCache.put(key, value);
        return "OK (Fallback)";
    }

    /**
     * get方法核心处理方式
     *
     * @param key 钥匙
     * @return {@link String }
     */
    public String get(String key) {
        try {
            if (redisAvailable) {
                return client.get(key);
            }
            return localCache.get(key);
        } catch (RedisClient.RedisException e) {
            return localCache.get(key);
        }
    }

    /**
     * mGet方法核心处理方式
     *
     * @param keys 按键
     * @return {@link List }<{@link String }>
     */
    public List<String> mget(String... keys) {
        List<String> results = new ArrayList<>();
        for (String key : keys) {
            results.add(get(key));
        }
        return results;
    }

    /**
     * del方法核心处理方式
     *
     * @param keys 按键
     * @return {@link Long }
     */
    public Long del(String... keys) {
        long count = 0;
        try {
            if (redisAvailable) {
                return client.del(keys);
            }
            // 降级处理
            for (String key : keys) {
                if (localCache.remove(key) != null) {
                    count++;
                }
            }
            return count;
        } catch (RedisClient.RedisException e) {
            // Redis操作失败时继续使用本地缓存
            for (String key : keys) {
                if (localCache.remove(key) != null) {
                    count++;
                }
            }
            return count;
        }
    }

    /**
     * lock方法核心处理方式
     *
     * @param lockType 锁类型
     * @param key      钥匙
     * @param ttl      ttl
     */
    public void lock(String lockType, String key, String ttl) {
        try {
            if (redisAvailable) {
                boolean isAsync = lockType.equalsIgnoreCase("ALOCK");
                RedisLock lock = new RedisLock(client, key, Integer.parseInt(ttl));
                if (isAsync) {
                    handleAsyncLock(lock, key);
                } else {
                    handleSyncLock(lock, key);
                }
            }else {
                // 降级到本地缓存
                System.out.println("本地缓存不可使用分布式锁");
            }
        } catch (RedisClient.RedisException e) {
            redisAvailable = false;
            System.out.println("本地缓存不可使用分布式锁");
        }
    }

    /**
     * 处理同步锁
     *
     * @param lock 锁
     * @param key  钥匙
     */
    private void handleSyncLock(RedisLock lock, String key) {
        lock.lock();
        activeLocks.put(key, lock);
        System.out.println("同步锁获取成功");
    }

    /**
     * 处理异步锁
     *
     * @param lock 锁
     * @param key  钥匙
     */
    private void handleAsyncLock(RedisLock lock, String key) {
        if (lock.tryLock()) {
            activeLocks.put(key, lock);
            System.out.println("异步锁获取成功");
        } else {
            System.out.println("异步锁获取失败（资源已被锁定）");
        }
    }

    /**
     * 解锁
     *
     * @param parts 零件
     */
    private void handleUnlock(String[] parts) {
        if (parts.length != 2) {
            throw new IllegalArgumentException("语法: UNLOCK key");
        }
        //防止释放不属于自己的锁
        RedisLock lock = activeLocks.remove(parts[1]);
        if (lock == null) {
            throw new IllegalStateException("没有找到该键对应的锁");
        }
        lock.unlock();
        System.out.println("锁释放成功");
    }

    //------------------------ 辅助方法 ------------------------//

    /**
     * 欢迎入口
     */
    private void printWelcome() {
        System.out.println("====================================");
        System.out.println(" Redis客户端");
        System.out.println(" 输入HELP查看可用命令");
        System.out.println("====================================");
    }

    /**
     * 帮助
     */
    private void printHelp() {
        System.out.println("支持命令：");
        System.out.println("SET key value [NX] [EX seconds] - 设置键值");
        System.out.println("GET key                        - 获取键值");
        System.out.println("MGET key1 [key2...]             - 批量获取");
        System.out.println("DEL key1 [key2...]             - 删除键");
        System.out.println("LOCK key ttl            - 获取同步锁");
        System.out.println("ALOCK key ttl           - 获取异步锁");
        System.out.println("UNLOCK key                    - 释放锁");
        System.out.println("EXIT                          - 退出程序");
    }

    /**
     * 退出服务
     */
    private void shutdown() {
        running = false;
        client.close();  // 改为调用客户端的关闭方法
        System.out.println("正在关闭...");
        System.exit(0);
    }
}